<script lang="ts" module>
	export type AssistantMessage = ChatMessage & {
		role: "assistant";
	};

	interface Props {
		message: AssistantMessage;
	}
</script>

<script lang="ts">
	import { LdrsLoader } from "$lib/components/buss/ldrs-loader/index.js";
	import { MarkdownRenderer } from "$lib/components/buss/markdown/index.js";
	import { ModelIcon } from "$lib/components/buss/model-icon/index.js";
	import {
		Collapsible,
		CollapsibleContent,
		CollapsibleTrigger,
	} from "$lib/components/ui/collapsible";
	import { m } from "$lib/paraglide/messages.js";
	import { getLocale } from "$lib/paraglide/runtime";
	import { chatState } from "$lib/stores/chat-state.svelte";
	import { mcpState } from "$lib/stores/mcp-state.svelte";
	import { preferencesSettings } from "$lib/stores/preferences-settings.state.svelte";
	import { tabBarState } from "$lib/stores/tab-bar-state.svelte";
	import { persistedThemeState } from "$lib/stores/theme.state.svelte";
	import type { ChatMessage } from "$lib/types/chat";
	import { ChevronDown, Lightbulb, Server } from "@lucide/svelte";
	import { toast } from "svelte-sonner";
	import MessageActions from "./message-actions.svelte";
	import MessageContextMenu from "./message-context-menu.svelte";
	import ToolCallModal from "./tool-call-modal.svelte";
	import { formatTimeAgo, getAssistantMessageContent } from "./utils";

	let { message }: Props = $props();

	function getServerIcon(toolName: string): string | null {
		// Extract server ID from toolName (format: serverId__toolName)
		const parts = toolName.split("__");
		if (parts.length >= 2) {
			const serverId = parts[0];
			const server = mcpState.getServer(serverId);
			return server?.icon || null;
		}

		return null;
	}

	function getServerName(toolName: string): string {
		// Extract server ID from toolName (format: serverId__toolName)
		const parts = toolName.split("__");
		if (parts.length >= 2) {
			const serverId = parts[0];
			const server = mcpState.getServer(serverId);
			return server?.name || m.tool_call_label();
		}

		return m.tool_call_label();
	}

	function getDisplayToolName(toolName: string): string {
		// Remove server ID prefix from display name
		const parts = toolName.split("__");
		return parts.length >= 2 ? parts.slice(1).join("__") : toolName;
	}

	let isReasoningExpanded = $state(!preferencesSettings.autoCollapseThink);
	let selectedToolPart = $state<any>(null);
	let isToolModalOpen = $state(false);

	$effect(() => {
		if (isStreamingReasoning) {
			isReasoningExpanded = true;
		} else if (!isCurrentMessageStreaming) {
			// When streaming ends, restore to the initial state based on settings
			isReasoningExpanded = !preferencesSettings.autoCollapseThink;
		}
	});

	const isCurrentMessageStreaming = $derived(
		chatState.isLastMessageStreaming && chatState.lastAssistantMessage?.id === message.id,
	);

	const hasReasoningContent = $derived(message.parts.some((part) => part.type === "reasoning"));
	const hasTextContent = $derived(message.parts.some((part) => part.type === "text"));
	const isStreamingReasoning = $derived(
		isCurrentMessageStreaming && hasReasoningContent && !hasTextContent,
	);
	const isStreamingText = $derived(
		isCurrentMessageStreaming && (hasTextContent || (!hasReasoningContent && !hasTextContent)),
	);

	async function handleCopyMessage() {
		try {
			await navigator.clipboard.writeText(getAssistantMessageContent(message));
			toast.success(m.toast_copied_success());
		} catch {
			toast.error(m.toast_copied_failed());
		}
	}

	function handleRegenerate() {
		chatState.regenerateMessage(message.id);
	}

	function handleDelete() {
		chatState.deleteMessage(message.id);
	}

	async function handleCreateBranch() {
		try {
			const newThreadId = await chatState.createBranch(message.id);
			if (newThreadId) {
				// Open the new thread in a new tab
				await tabBarState.handleNewTabForExistingThread(newThreadId);
			} else {
				toast.error(m.toast_unknown_error());
			}
		} catch (error) {
			console.error("Failed to create branch:", error);
			toast.error(m.toast_unknown_error());
		}
	}
</script>

{#snippet messageHeader(model: string)}
	<div class="flex items-center gap-2">
		<ModelIcon className="size-6" modelName={model} />
		<span class="text-xs text-muted-foreground">{model}</span>
	</div>
{/snippet}

{#snippet messageFooter()}
	<div class="flex items-center gap-2 opacity-0 group-hover:opacity-100">
		<MessageActions {message} enabledActions={["copy", "regenerate"]} />
		<span class="text-xs text-muted-foreground">
			{formatTimeAgo(message.metadata?.createdAt?.toLocaleString() || "", getLocale())}
		</span>
	</div>
{/snippet}

<MessageContextMenu
	onCopy={handleCopyMessage}
	onRegenerate={handleRegenerate}
	onCreateBranch={handleCreateBranch}
	onDelete={handleDelete}
>
	<div class="group flex flex-col gap-1">
		{@render messageHeader(message.metadata?.model || "gpt-4o")}

		{#each message.parts as part, partIndex (partIndex)}
			{#if part.type === "text"}
				{#if preferencesSettings.autoDisableMarkdown}
					<div class="whitespace-pre-wrap text-sm leading-relaxed">
						{part.text}
					</div>
				{:else}
					<MarkdownRenderer
						content={part.text}
						codeTheme={persistedThemeState.current.shouldUseDarkColors
							? "vitesse-dark"
							: "vitesse-light"}
					/>
				{/if}
			{:else if part.type === "reasoning"}
				{#if !preferencesSettings.autoHideReason}
					<Collapsible bind:open={isReasoningExpanded} class="rounded-lg border bg-muted/30 p-3">
						<CollapsibleTrigger
							class="flex w-full items-center justify-between text-left transition-colors hover:bg-muted/20 rounded-md p-2 -m-2"
						>
							<div class="flex items-center gap-2">
								<Lightbulb class="h-4 w-4 text-muted-foreground" />
								<span class="text-sm font-medium text-muted-foreground">{m.title_thinking()}</span>
							</div>
							<ChevronDown
								class="h-4 w-4 text-muted-foreground transition-transform duration-200 {isReasoningExpanded
									? 'rotate-180'
									: ''}"
							/>
						</CollapsibleTrigger>
						<CollapsibleContent class="space-y-2">
							<div class="pt-3 text-xs text-muted-foreground leading-relaxed whitespace-pre-wrap">
								{part.text}
							</div>

							{#if isStreamingReasoning}
								<div class="flex items-center gap-2 pt-2 animate-in fade-in duration-300">
									<LdrsLoader
										type="dot-pulse"
										size={16}
										speed={1.2}
										color={persistedThemeState.current.shouldUseDarkColors ? "#a1a1aa" : "#71717a"}
									/>
									<span class="text-xs text-muted-foreground italic">
										{m.title_thinking()}...
									</span>
								</div>
							{/if}
						</CollapsibleContent>
					</Collapsible>
				{/if}
			{:else if part.type === "dynamic-tool"}
				<button
					type="button"
					class="my-2 block w-full cursor-pointer rounded-[10px] border-0 bg-white px-3.5 py-3 text-left hover:bg-[#F9F9F9] dark:bg-[#1A1A1A] dark:hover:bg-[#2D2D2D]"
					onclick={() => {
						selectedToolPart = part;
						isToolModalOpen = true;
					}}
				>
					<div class="flex w-full items-center justify-between gap-x-4">
						<!-- Left: Tool Icon and Name -->
						<div class="flex items-center gap-3">
							<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-muted">
								{#if getServerIcon(part.toolName)}
									<span class="text-xl">{getServerIcon(part.toolName)}</span>
								{:else}
									<Server class="h-5 w-5 text-muted-foreground" />
								{/if}
							</div>

							<!-- Tool Name -->
							<div class="flex flex-col items-start gap-1">
								<h3 class="text-sm font-medium text-foreground">
									{getDisplayToolName(part.toolName)}
								</h3>
								<p class="text-xs text-muted-foreground">{getServerName(part.toolName)}</p>
							</div>
						</div>

						<!-- Right: Status -->
						<div class="flex items-center gap-2">
							{#if part.state === "input-streaming"}
								<div class="h-2 w-2 animate-pulse rounded-full bg-[#0056FE]"></div>
								<span class="text-sm text-[#0056FE]">{m.tool_call_status_preparing()}</span>
							{:else if part.state === "input-available"}
								<div class="h-2 w-2 animate-pulse rounded-full bg-[#0056FE]"></div>
								<span class="text-sm text-[#0056FE]">{m.tool_call_status_executing()}</span>
							{:else if part.state === "output-available"}
								<div class="h-2 w-2 rounded-full bg-[#38B865]"></div>
								<span class="text-sm text-[#38B865]">{m.tool_call_status_success()}</span>
							{:else if part.state === "output-error"}
								<div class="h-2 w-2 rounded-full bg-[#D82525]"></div>
								<span class="text-sm text-[#D82525]">{m.tool_call_status_error()}</span>
							{/if}
						</div>
					</div>
				</button>
			{/if}
		{/each}

		{#if isStreamingText}
			<div class="flex items-center gap-3 py-3 animate-in fade-in duration-300">
				<div class="flex items-center justify-center">
					<LdrsLoader
						type="dot-pulse"
						size={24}
						speed={1.2}
						color={persistedThemeState.current.shouldUseDarkColors ? "#a1a1aa" : "#71717a"}
					/>
				</div>
				<span class="text-sm text-muted-foreground italic">
					{m.text_chat_responding()}...
				</span>
			</div>
		{/if}

		<!-- Tool Call Modal -->
		{#if selectedToolPart}
			<ToolCallModal
				part={selectedToolPart}
				messageId={message.id}
				bind:open={isToolModalOpen}
				onOpenChange={(open) => {
					isToolModalOpen = open;
					if (!open) {
						selectedToolPart = null;
					}
				}}
			/>
		{/if}

		{@render messageFooter()}
	</div>
</MessageContextMenu>
